/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.openfile; import java.beans.*; import java.net.*; import java.io.*; import java.util.*; import org.openide.*; import org.openide.loaders.DataObject; import org.openide.util.NbBundle; /** Acts as UDP server for requests from the Main class (for example). * @author Jaroslav Tulach, Jesse Glick */ public class Server extends Object implements Runnable { /** max length of transferred data */ private static final int LENGTH = 512; /** true if we should stop due to uninstallation */ private static boolean stop; static void startup () { //System.err.println("Server.startup"); stop = false; if (! Settings.DEFAULT.isActualRunning ()) { //System.err.println("Server.startup: starting thread"); new Thread (new Server (), "OpenFile Server").start (); // NOI18N } } public static void shutdown () { //System.err.println("Server.shutdown"); stop = true; if (Settings.DEFAULT.isActualRunning ()) { //System.err.println("Server.shutdown: will actually send shutdown packet"); try { int port = Settings.DEFAULT.getActualPort (); if (port == 0) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N System.err.println ("Port should not have been zero during shutdown!"); return; } DatagramPacket p = new DatagramPacket (new byte[] { (byte) 'X' }, 1, InetAddress.getLocalHost (), port); DatagramSocket s = new DatagramSocket (); try { s.send (p); } finally { s.close (); } } catch (IOException ioe) { ioe.printStackTrace (); } } } /** the socket to use */ private static DatagramSocket s; /** set up the socket */ private static void initSocket () { //System.err.println("Server.initSocket"); if (s != null) s.close (); int port = Settings.DEFAULT.getPort (); try { s = new DatagramSocket (port); Settings.DEFAULT.setActualPort0 (port); } catch (SocketException e) { TopManager.getDefault ().notify (new NotifyDescriptor.Message (SettingsBeanInfo.getString ("MSG_cannotBind", new Integer (port)))); Settings.DEFAULT.setRunning (false); Settings.DEFAULT.setActualPort0 (0); } } /** Waits on the connection. */ public void run () { //System.err.println("Server.run"); initSocket (); DatagramPacket p = new DatagramPacket (new byte[LENGTH], LENGTH); try { REQUESTS: while (! stop && Settings.DEFAULT.isRunning ()) { //System.err.println("Server.run @REQUESTS"); if (Settings.DEFAULT.getPort () != Settings.DEFAULT.getActualPort ()) initSocket (); if (! Settings.DEFAULT.isActualRunning ()) Settings.DEFAULT.setActualRunning0 (true); p.setLength (LENGTH); //System.err.println("Server.run: will receive"); s.receive (p); //System.err.println("Server.run: received"); // Check access: if (Settings.DEFAULT.getAccess () == Settings.ACCESS_LOCAL) { if (! p.getAddress ().equals (InetAddress.getLocalHost ())) { TopManager.getDefault ().notify (new NotifyDescriptor.Message (SettingsBeanInfo.getString ("MSG_rejectHost", p.getAddress ()))); continue; } } // Try to open the requested file: String fileName = new String (p.getData (), p.getOffset () + 1, p.getLength () - 1); int lineNumber; int index = fileName.indexOf ('@'); if (index == -1) { lineNumber = -1; } else { try { lineNumber = Integer.parseInt (fileName.substring (index + 1)) - 1; fileName = fileName.substring (0, index); } catch (NumberFormatException nfe) { TopManager.getDefault ().notifyException (nfe); lineNumber = -1; } } //System.err.println("Server.run: to open " + fileName + " at " + lineNumber); final boolean wait; switch ((char) (p.getData ()[p.getOffset ()])) { case 'Y': wait = true; break; case 'N': wait = false; break; case 'X': if (stop) break REQUESTS; else continue REQUESTS; default: throw new IOException (NbBundle.getBundle (Server.class).getString ("EXC_bad_lead_char")); } byte res; final File f = new File (fileName); if (f.exists () && f.isFile ()) { res = 0; final InetAddress addr = p.getAddress (); final int port = p.getPort (); // Opening may be slow, incl. prompting for mount point. Should not time out launcher while waiting for user. final int _lineNumber = lineNumber; new Thread (new Runnable () { public void run () { //System.err.println("Server.run: to call open on f=" + f + " wait=" + wait + " addr=" + addr + " port=" + port + " lineNumber=" + _lineNumber); OpenFile.open (f, wait, addr, port, _lineNumber); } }).start (); } else { // Do not time out launcher while waiting for user to press OK. // XXX SwingUtilities.invokeLater? final String _fileName = fileName; new Thread (new Runnable () { public void run () { TopManager.getDefault ().notify (new NotifyDescriptor.Message (SettingsBeanInfo.getString ("MSG_fileNotFound", _fileName))); } }).start (); res = 1; } // send reply (unless we are waiting for file to be saved) if (!wait || res != 0) { //System.err.println("Server.run: will send response res=" + res); p.getData ()[0] = res; p.setLength (1); s.send (p); } } } catch (IOException ioe) { ioe.printStackTrace (); Settings.DEFAULT.setActualRunning0 (false); Settings.DEFAULT.setRunning (false); } finally { //System.err.println("Server.run: done"); if (s != null) s.close (); s = null; Settings.DEFAULT.setActualRunning0 (false); Settings.DEFAULT.setActualPort0 (0); } } /** Addresses of waiting launchers, based on the DO they wait on. * @associates InetAddress*/ private static final Map addresses = new HashMap (); // Map<DataObject, InetAddress> /** Ports of waiting launchers. * @associates Integer*/ private static final Map ports = new HashMap (); // Map<DataObject, Integer> /** Listener on all waiting DOs that notices when they are saved (or deleted). */ private static final PropertyChangeListener waitingListener = new PropertyChangeListener () { public void propertyChange (PropertyChangeEvent ev) { DataObject obj = (DataObject) ev.getSource (); if (DataObject.PROP_VALID.equals (ev.getPropertyName ())) { // If destroyed, report an error. if (! obj.isValid ()) { unWait (obj, (byte) 1); } } else if (DataObject.PROP_MODIFIED.equals (ev.getPropertyName ())) { // Don't do anything when it *becomes* modified, only when unmodified. if (! obj.isModified ()) { unWait (obj, (byte) 0); } } } /** Notify the launcher that it is done waiting on a DO. */ private void unWait (DataObject obj, byte status) { obj.removePropertyChangeListener (this); if (s != null) { InetAddress addr = (InetAddress) addresses.remove (obj); Integer port = (Integer) ports.remove (obj); DatagramPacket p = new DatagramPacket (new byte[] { status }, 1, addr, port.intValue ()); try { s.send (p); } catch (IOException e) { TopManager.getDefault ().notifyException (e); } } else { TopManager.getDefault ().notify (new NotifyDescriptor.Message (SettingsBeanInfo.getString ("MSG_serverNotRunningWhenSaved", obj.getName ()))); } } }; /** Register a callback so that the launcher will be notified when the file is modified & saved. * @param obj the object to wait for * @param addr the address to send a message back to * @param port the port to send a message back to */ static void waitFor (DataObject obj, InetAddress addr, int port) { addresses.put (obj, addr); ports.put (obj, new Integer (port)); obj.addPropertyChangeListener (waitingListener); } /** Test run. */ /* public static void main (String[] args) { startup (); } */ } /* * Log * 12 Gandalf 1.11 1/12/00 Jesse Glick I18N. * 11 Gandalf 1.10 1/12/00 Ales Novak can be compiled under * 1.3 * 10 Gandalf 1.9 1/7/00 Jesse Glick -line option for line * numbers. * 9 Gandalf 1.8 1/4/00 Jesse Glick Accessibility for * installation from Utils module. * 8 Gandalf 1.7 11/2/99 Jesse Glick Overhauled socket * handling. * 7 Gandalf 1.6 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 6 Gandalf 1.5 9/10/99 Jesse Glick #3647 - * SocketException's on Linux + native threads. * 5 Gandalf 1.4 8/17/99 Jesse Glick Changed handling of * return status code to be more immediate and simplified. Fixes #2420 and * #3297. * 4 Gandalf 1.3 7/29/99 Jesse Glick Abortive attempt at * fixing #2966. * 3 Gandalf 1.2 7/29/99 Jesse Glick Bugfix #2755. * 2 Gandalf 1.1 7/10/99 Jesse Glick Open File module moved * to core. * 1 Gandalf 1.0 7/10/99 Jesse Glick * $ */